/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ctl.utils.io;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.*;
import javax.swing.*;
import org.apache.log4j.Logger;

/**
 *
 * @author ctl
 */
public class NIOServerUtil extends javax.swing.JFrame {

    /**
     * Creates new form NIOServerUtil
     */
    public NIOServerUtil() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        IPLabel = new javax.swing.JLabel();
        jTextFieldIP = new javax.swing.JTextField();
        jLabelPort = new javax.swing.JLabel();
        jTextFieldPort = new javax.swing.JTextField();
        startServer = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextAreaChat = new javax.swing.JTextArea();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        jListClientsLink = new javax.swing.JList();
        jLabel3 = new javax.swing.JLabel();
        jScrollPane3 = new javax.swing.JScrollPane();
        jTextAreaContentToSend = new javax.swing.JTextArea();
        closeClientLink = new javax.swing.JButton();
        clearSendMessage = new javax.swing.JButton();
        send = new javax.swing.JButton();
        clearChatContent = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("NIOServerUtil");

        IPLabel.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        IPLabel.setText("IP:");

        jTextFieldIP.setEditable(false);
        jTextFieldIP.setBackground(new java.awt.Color(255, 255, 255));
        jTextFieldIP.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        try{
            jTextFieldIP.setText(InetAddress.getLocalHost().getHostAddress());
            jTextFieldIP.setAutoscrolls(false);
        }catch(IOException e){
            logger.info(e.getMessage());
        }

        jLabelPort.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jLabelPort.setText("PORT:");

        jTextFieldPort.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jTextFieldPort.setText("1234");

        startServer.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        startServer.setText("开始服务");
        startServer.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                startServerMouseClicked(evt);
            }
        });

        jLabel1.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jLabel1.setText("服务器信息");

        jTextAreaChat.setEditable(false);
        jTextAreaChat.setBackground(new java.awt.Color(240, 240, 240));
        jTextAreaChat.setColumns(20);
        jTextAreaChat.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jTextAreaChat.setLineWrap(true);
        jTextAreaChat.setRows(5);
        jTextAreaChat.setWrapStyleWord(true);
        jScrollPane1.setViewportView(jTextAreaChat);

        jLabel2.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jLabel2.setText("已经建立的连接");

        jListClientsLink.setBackground(new java.awt.Color(240, 240, 240));
        jListClientsLink.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jListClientsLink.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jListClientsLinkMouseClicked(evt);
            }
        });
        jScrollPane2.setViewportView(jListClientsLink);

        jLabel3.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jLabel3.setText("待发送数据");

        jTextAreaContentToSend.setBackground(new java.awt.Color(255, 255, 255));
        jTextAreaContentToSend.setColumns(20);
        jTextAreaContentToSend.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        jTextAreaContentToSend.setRows(5);
        jScrollPane3.setViewportView(jTextAreaContentToSend);

        closeClientLink.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        closeClientLink.setText("断开连接");
        closeClientLink.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                closeClientLinkMouseClicked(evt);
            }
        });

        clearSendMessage.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        clearSendMessage.setText("清除");
        clearSendMessage.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                clearSendMessageMouseClicked(evt);
            }
        });

        send.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        send.setText("发送数据");
        send.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                sendMouseClicked(evt);
            }
        });

        clearChatContent.setFont(new java.awt.Font("新宋体", 0, 10)); // NOI18N
        clearChatContent.setText("清除");
        clearChatContent.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                clearChatContentMouseClicked(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createSequentialGroup()
                                .addContainerGap()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addComponent(jScrollPane1)
                                        .addComponent(jScrollPane2)
                                        .addComponent(jScrollPane3)
                                        .addGroup(layout.createSequentialGroup()
                                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                        .addComponent(jLabel1)
                                                        .addGroup(layout.createSequentialGroup()
                                                                .addComponent(IPLabel)
                                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                .addComponent(jTextFieldIP, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                                .addComponent(jLabelPort)
                                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                .addComponent(jTextFieldPort, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                .addComponent(startServer))
                                                        .addComponent(jLabel3))
                                                .addGap(0, 3, Short.MAX_VALUE))
                                        .addGroup(layout.createSequentialGroup()
                                                .addComponent(jLabel2)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                .addComponent(clearChatContent))
                                        .addGroup(layout.createSequentialGroup()
                                                .addComponent(closeClientLink)
                                                .addGap(56, 56, 56)
                                                .addComponent(clearSendMessage)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                .addComponent(send)))
                                .addContainerGap())
        );
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createSequentialGroup()
                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                        .addComponent(IPLabel)
                                        .addComponent(jTextFieldIP, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addComponent(jLabelPort)
                                        .addComponent(jTextFieldPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addComponent(startServer))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel1)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 307, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                        .addComponent(jLabel2)
                                        .addComponent(clearChatContent))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 113, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addComponent(jLabel3)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 147, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                        .addComponent(closeClientLink)
                                        .addComponent(clearSendMessage)
                                        .addComponent(send)))
        );

        pack();
    }// </editor-fold>
    private static Logger logger = Logger.getLogger(NIOServerUtil.class);
    private void startServerMouseClicked(java.awt.event.MouseEvent evt) {
        if (startServer.getText().trim().equals("开始服务")) {
            class NioServerChannelThread implements Runnable {

                public void run() {
                    try {
                        selector = Selector.open();
                        // 通过open方法来打开一个未绑定的ServerSocketChannel实例
                        server = ServerSocketChannel.open();
                        InetSocketAddress isa = new InetSocketAddress(
                                jTextFieldIP.getText().trim(), Integer.parseInt(jTextFieldPort.getText()));
                        server.socket().bind(isa);
                        // 设置ServerScoket以非阻塞的方式工作
                        server.configureBlocking(false);
                        // 将server注册到指定Selector对象
                        // static int OP_ACCEPT 用于套接字接受操作的操作集位。
                        // static int OP_CONNECT 用于套接字连接操作的操作集位。
                        // static int OP_READ 用于读取操作的操作集位。
                        // static int OP_WRITE 用于写入操作的操作集位。
                        server.register(selector, SelectionKey.OP_ACCEPT);
                        // while ((selectorSelectSize=selector.select()) > 0)
                        // {//只要selecet。select()<=0就会阻塞到这里
                        while (selector.select() > 0) {// 只要selecet。select()<=0就会阻塞到这里
                            // System.out.println("selector.select():"+selectorSelectSize);
                            // 依次处理selector上的每个已选择的SelectionKey
                            for (SelectionKey sk : selector.selectedKeys()) {
                                // 从selector上的以选择key集中删除正在处理的SelectionKey
                                selector.selectedKeys().remove(sk);
                                // 如果sk对应的通道包含客户端的连接请求
                                if (sk.isAcceptable()) {
                                    // 调用accept方法接受连接，产生服务器对应的SocketChannel
                                    SocketChannel sc = server.accept();
                                    //   socketChannel.add(sc);
                                    //   jListClientsLink.setListData(socketChannel.toArray(new String[]{}));
                                    System.err.println("客户端连接channel:" + sc + "   --- getRemoteAddress:" + sc.getRemoteAddress() + "**");
                                    // 设置采用非阻塞模式
                                    sc.configureBlocking(false);
                                    // 将该SocketChannel也注册到selector
                                    sc.register(selector, SelectionKey.OP_READ);
                                    // 将sk对应的Channel设置成准备接受其他请求
                                    sk.interestOps(SelectionKey.OP_ACCEPT);
                                    socketChannelList.add(sc);
                                    clientLinkList.add(sc.getRemoteAddress().toString().substring(1));
                                    jListClientsLink.setListData(clientLinkList.toArray(new String[]{}));
                                }
                                // 如果sk对应的通道有数据需要读取
                                if (sk.isReadable()) {
                                    // 获取该SelectionKey对应的Channel，该Channel中有可读的数据
                                    SocketChannel sc = (SocketChannel) sk.channel();
                                    // 定义准备执行读取数据的bytebuffer
                                    ByteBuffer buff = ByteBuffer.allocate(1024);
                                    String content = "";
                                    try {
                                        while (sc.read(buff) > 0) {
                                            buff.flip();
                                            content += charset.decode(buff)
                                                    .toString();
                                        }
                                        if (content.trim().length() < 1) {
                                            socketChannelList.remove(sc);
                                            System.out.println("客户端关闭");
                                            socketChannelList.remove(sc);
                                            clientLinkList.remove(sc.getRemoteAddress().toString().substring(1));
                                            jListClientsLink.setListData(clientLinkList.toArray(new String[]{}));
                                            selectIndexs = new int[0];
                                            sc.close();
                                            continue;
                                        }
                                        chatContent.append(sc.getRemoteAddress().toString().substring(1)).append(":");
                                        chatContent.append(content).append("\n");
                                        jTextAreaChat.setText(chatContent.toString());
                                        jTextAreaChat.setCaretPosition(chatContent.length());
                                        // 将sk对用的Channel设置成准备下一次读取
                                        sk.interestOps(SelectionKey.OP_READ);
                                    } catch (IOException ex) {// 客户端连接关闭
                                        selectIndexs = new int[0];
                                        for (int size = 0; size < clientLinkList.size(); size++) {
                                            SocketChannel socketChannel = (SocketChannel) sk.channel();
                                            if (clientLinkList.contains(socketChannel.getRemoteAddress().toString().substring(1))) {
                                                clientLinkList.remove(socketChannel.getRemoteAddress().toString().substring(1));
                                            }
                                            if (socketChannelList.contains(socketChannel)) {
                                                socketChannelList.remove(socketChannel);
                                                socketChannel.shutdownInput();
                                                socketChannel.shutdownOutput();
                                                break;
                                            }
                                        }
                                        sk.cancel();
                                        if (sk.channel() != null) {
                                            sk.channel().close();
                                        }
                                        jListClientsLink.setListData(clientLinkList.toArray(new String[]{}));
                                        System.err.println("2:" + ex.getMessage());
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        System.err.println("1" + e.getMessage());
                    }
                }
            }
            new Thread(new NioServerChannelThread()).start();
            startServer.setText("停止服务");
        } else {
            try {
                for (SelectionKey sk : selector.keys()) {
                    sk.cancel();
                    sk.channel().close();
                }
                for (SelectionKey sk : selector.selectedKeys()) {
                    sk.cancel();
                    SocketChannel dest = (SocketChannel) sk.channel();
                    dest.socket().close();
                    sk.channel().close();
                }
                server.close();
                selector.close();
            } catch (Exception e) {
                System.out.println("close server err:" + e.getMessage());
            }
            selectIndexs = new int[0];
            startServer.setText("开始服务");
            jListClientsLink.setListData(new String[]{});
            socketChannelList.clear();
            clientLinkList.clear();
            jTextAreaChat.setText(null);
            chatContent.delete(0, chatContent.length());
        }
    }

    private void closeClientLinkMouseClicked(java.awt.event.MouseEvent evt) {
        ListModel mode1 = jListClientsLink.getModel();
        try {
            for (int ind : selectIndexs) {
                String tmp = (String) mode1.getElementAt(ind);
                System.out.println("\nselect " + ind + " " + tmp);
                for (int size = 0; size < clientLinkList.size(); size++) {
                    if (clientLinkList.contains(tmp)) {
                        clientLinkList.remove(tmp);
                    }
                    SocketChannel tmpSocketChannel = socketChannelList
                            .get(size);
                    if (tmpSocketChannel.getRemoteAddress().toString()
                            .substring(1).equals(tmp)) {
                        socketChannelList.remove(tmpSocketChannel);
                        tmpSocketChannel.shutdownInput();
                        tmpSocketChannel.shutdownOutput();
                        break;
                    }
                }
            }
        } catch (IOException e) {
            System.out.println("cloee link err:" + e.getMessage());
        }
        selectIndexs = new int[0];
        jListClientsLink.setListData(clientLinkList.toArray(new String[]{}));
    }

    private void clearChatContentMouseClicked(java.awt.event.MouseEvent evt) {
        chatContent.delete(0, chatContent.length()-1);
        jTextAreaChat.setText(null);
    }

    private void clearSendMessageMouseClicked(java.awt.event.MouseEvent evt) {
        jTextAreaContentToSend.setText(null);
    }

    private void sendMouseClicked(java.awt.event.MouseEvent evt) {
        if (jTextAreaContentToSend.getText() == null || jTextAreaContentToSend.getText().trim().equals("")) {
            return;
        }
        if (null != selectIndexs && selectIndexs.length > 0) {
            for (int index : selectIndexs) {
                SocketChannel tmpSocketChannel = socketChannelList.get(index);
                try {
                    tmpSocketChannel.write(charset.encode(jTextAreaContentToSend.getText()));
                    chatContent.append("Server Send:").append(jTextAreaContentToSend.getText()).append("\n");
                    jTextAreaChat.setText(chatContent.toString());
                } catch (Exception e) {
                    System.out.println("send err:" + e.getMessage());
                }
            }
        }
    }

    private void jListClientsLinkMouseClicked(java.awt.event.MouseEvent evt) {
        selectIndexs = jListClientsLink.getSelectedIndices();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(NIOServerUtil.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(NIOServerUtil.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(NIOServerUtil.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(NIOServerUtil.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new NIOServerUtil().setVisible(true);
            }
        });
    }
    private int[] selectIndexs = null;
    //用于检测所有的Channel状态Selector
    private Selector selector = null;
    //定义实现编码、解码的字符集对象
    private final Charset charset = Charset.forName("gbk");
    // 通过open方法来打开一个未绑定的ServerSocketChannel实例
    ServerSocketChannel server;
    private final StringBuilder chatContent = new StringBuilder();
    private final List<SocketChannel> socketChannelList = new ArrayList<SocketChannel>();
    private final List<String> clientLinkList = new ArrayList<String>();
    // Variables declaration - do not modify
    private javax.swing.JLabel IPLabel;
    private javax.swing.JButton clearChatContent;
    private javax.swing.JButton clearSendMessage;
    private javax.swing.JButton closeClientLink;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabelPort;
    private javax.swing.JList jListClientsLink;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JTextArea jTextAreaChat;
    private javax.swing.JTextArea jTextAreaContentToSend;
    private javax.swing.JTextField jTextFieldIP;
    private javax.swing.JTextField jTextFieldPort;
    private javax.swing.JButton send;
    private javax.swing.JButton startServer;
    // End of variables declaration                   

}
